#!/usr/bin/env python

import gtk
import gobject
import urllib
import time
import base64
import os, sys
import gettext
import socket
from mxit import connection as c
from mxit import encryption
from signals import accountCreatorAssistantSignals
from blend import account
from blend import config

gettext.install('blendIT','locale');
debug = False;

class accountCreatorAssistant(gtk.Assistant):

   def __init__(self,blend):
      gtk.Assistant.__init__(self);
      self.set_title(_("Account Creator"));
      self.set_icon_from_file("images/blendit.png");
      self.sigs = accountCreatorAssistantSignals.signals(blend);
      self.blend = blend;
      
      self.connect('apply',self.sigs.apply);
      self.connect('cancel',self.sigs.cancel);
      self.connect('close',self.sigs.close);
      self.connect('prepare',self.sigs.prep);

      
      self.beginPage();
      self.accountDetailsPage();
      self.captchaPage();
      self.completedPage();     
      self.set_current_page(0);
      self.set_border_width(4);
      self.show();
   

   ### Pages ###
   def beginPage(self):
      page = gtk.VBox();

      
      # Stuff
      message = _("Welcome to the Account Creator.");
      message += _(" This assistant will help you to add your MXit account to BlendIt.\n");
      message += _("If you do not already have a MXit account, you can create one right now with the Account Creator!");
      message += _("\n\nPlease remember: Currently you may only have one account setup in BlendIt.\n");
      message += _("Using the account creator will overwrite any previously stored accounts.");
      label = gtk.Label(message);
      label.show();
      page.pack_start(label,True,False,16);
      page.show();
      
      self.append_page(page)
      self.set_page_title(page,_("BlendIt Account Creator"))
      self.set_page_type(page,gtk.ASSISTANT_PAGE_INTRO);
      self.set_page_complete(page,True);



   # Fill in details about the account that we need
   def accountDetailsPage(self):
      self.getData('challenge','getcountries=true&getlanguage=true');
      if not self.parseData():
         self.errorMessageDialog(_("Could not get data from MXit"));
         self.fail();
         return;
      
      page = gtk.Table(rows=6,columns=2);
      page.set_row_spacings(8);
      

      self.entries = [];

      # Content:
      self.entries.append(gtk.CheckButton(_("Create a new MXit account")));
      self.entries[0].connect('toggled',self.sigs.checkCreateAccount);
      page.attach(self.entries[0],0,1,0,1,gtk.FILL);
      
      page.attach(gtk.Label(_("Cell Number: ")),0,1,1,2,gtk.FILL);
      self.entries.append(gtk.Entry());
      self.entries[1].connect('changed',self.sigs.checkEntries);
      alignOne = gtk.Alignment(xalign=0);      
      alignOne.add(self.entries[1]);
      self.cellInfoLabel = gtk.Label();
      self.cellInfoLabel.show();
      cellVbox = gtk.HBox(spacing=3);
      cellVbox.pack_start(alignOne);
      cellVbox.pack_end(self.cellInfoLabel);
      cellVbox.show_all();
      page.attach(cellVbox,1,2,1,2,gtk.FILL);

      page.attach(gtk.Label(_("Nickname: ")),0,1,2,3,gtk.FILL);
      self.entries.append(gtk.Entry());
      self.entries[2].set_sensitive(False);
      self.entries[2].connect('changed',self.sigs.checkEntries);
      alignTwo = gtk.Alignment(xalign=0);      
      alignTwo.add(self.entries[2]);
      page.attach(alignTwo,1,2,2,3,gtk.EXPAND|gtk.FILL);

      page.attach(gtk.Label(_("Pin: ")),0,1,3,4,gtk.FILL);
      self.entries.append(gtk.Entry());
      self.entries[3].set_visibility(False);
      self.entries[3].set_max_length(10);
      self.entries[3].show();
      self.entries[3].connect('changed',self.sigs.checkEntries);
      alignThree = gtk.Alignment(xalign=0);      
      alignThree.add(self.entries[3]);
      self.pinInfoLabel = gtk.Label('');
      self.pinInfoLabel.show();
      pinVbox = gtk.HBox(spacing=3);
      pinVbox.pack_start(alignThree);
      pinVbox.pack_end(self.pinInfoLabel);
      pinVbox.show_all();
      page.attach(pinVbox,1,2,3,4,gtk.FILL);

   	# Country
      page.attach(gtk.Label(_("Country: ")),0,1,4,5,gtk.FILL);
      model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING);
      for i in range(0,len(self.countries)):
         key = self.countries[i][1];
         value = self.countries[i][0];
         model.append((value,key));

      self.entries.append(gtk.ComboBox(model));
      self.entries[4].set_active(self.defaultCountry);
      txt = gtk.CellRendererText();
      self.entries[4].pack_start(txt,True);
      self.entries[4].add_attribute(txt,'text',0);
      self.entries[4].show();
      alignFour = gtk.Alignment(xalign=0);      
      alignFour.add(self.entries[4]);
      page.attach(alignFour,1,2,4,5,gtk.FILL);


      ### LANGUAGE ###
      l = os.getenv('LANG');
      if l is None:
         l = "en";
      else:
         l = l[:2];
      default = self.search(self.languages,l);
      if default == -1:
         default = 3;

      index = 0;
      page.attach(gtk.Label(_("Language: ")),0,1,5,6,gtk.FILL);
      model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING);
      for i in range(0,len(self.languages)):
         key = self.languages[i][1];
         value = self.languages[i][0];
         model.append((value,key));
         if key == self.languages[default][1]:
            index = i;

      self.entries.append(gtk.ComboBox(model));
      self.entries[5].set_active(index);
      txt = gtk.CellRendererText();
      self.entries[5].pack_start(txt,True);
      self.entries[5].add_attribute(txt,'text',0);
      self.entries[5].show();
      self.entries[5].connect('changed',self.sigs.checkLang);
      alignFive = gtk.Alignment(xalign=0);      
      alignFive.add(self.entries[5]);
      alignFive.show();
      self.langInfoLabel = gtk.Label();
      self.langInfoLabel.show();
      langVbox = gtk.HBox(spacing=3);
      langVbox.pack_start(alignFive);
      langVbox.pack_end(self.langInfoLabel);
      langVbox.show_all();
      page.attach(langVbox,1,2,5,6,gtk.FILL);

      # Date of Birth
      label = gtk.Label(_("Date of Birth: "));
      label.show();
      alignLabel = gtk.Alignment(xalign=0.5,yalign=0);
      alignLabel.add(label);
      alignLabel.show();
      page.attach(alignLabel,0,1,6,7,gtk.FILL);
      self.entries.append(gtk.Calendar());
      self.entries[6].show();
      self.entries[6].set_sensitive(False);
      self.entries[6].connect('day-selected',self.sigs.checkDate);
      self.entries[6].connect('next-year',self.sigs.checkDate);
      self.entries[6].connect('next-month',self.sigs.checkDate);
      self.calInfoLabel = gtk.Label();
      self.calInfoLabel.set_justify(gtk.JUSTIFY_CENTER);
      self.calInfoLabel.show();
      dobHbox = gtk.HBox();
      dobHbox.pack_start(self.entries[6]);
      dobHbox.pack_start(self.calInfoLabel);
      alignSix = gtk.Alignment(xalign=0,yalign=0.5);
      alignSix.add(dobHbox);
      alignSix.show();
      page.attach(alignSix,1,2,6,7,gtk.FILL); 

      # Gender
      page.attach(gtk.Label(_("Gender: ")),0,1,7,8,gtk.FILL);
      male = gtk.RadioButton(None,_("Male"));
      male.connect('toggled',self.sigs.checkEntries);
      male.set_sensitive(False);
      male.show();
      female = gtk.RadioButton(male,_("Female"));
      female.connect('toggled',self.sigs.checkEntries);
      female.set_sensitive(False);
      female.show();
      self.entries.append((male,female));
      gendVbox = gtk.VBox();
      gendVbox.pack_start(self.entries[7][0]);
      gendVbox.pack_start(self.entries[7][1]);
      gendVbox.show();
      alignSev = gtk.Alignment(xalign=0);
      alignSev.add(gendVbox);
      alignSev.show();
      page.attach(alignSev,1,2,7,8,gtk.FILL); 
      
      page.show_all();
      self.append_page(page);
      self.set_page_title(page,_("Step 1: Account Details"))
      self.set_page_type(page,gtk.ASSISTANT_PAGE_CONTENT);


   def captchaPage(self):
      page = gtk.VBox();
      page.show();

      # Content
      page.set_spacing(6);
      align = gtk.Alignment(xalign=0.5,yalign=0.5);
      vbox = gtk.VBox(spacing=10);
      vbox.pack_start(gtk.Label(_("Please enter the numbers from the image in the text box below")));
      
      self.captchaImage = gtk.Image();
      vbox.pack_start(self.captchaImage);
      self.captchaEntry = gtk.Entry();
      self.captchaEntry.set_width_chars(8);
      
      vbox.pack_start(self.captchaEntry,False,False);
      self.captchaEntry.connect('changed',self.sigs.checkCaptcha);
      vbox.show_all();
      align.add(vbox);
      align.show_all();
      
      page.pack_start(align,True,True);  
      page.show_all();    

      self.append_page(page);
      self.set_page_title(page,_("Step 2: Enter Captcha"))
      self.set_page_type(page,gtk.ASSISTANT_PAGE_CONTENT);


   def completedPage(self):
      page = gtk.VBox();
      page.show();

      # Stuff
      if self.entries[0].get_active():
         message = _("You have successfully registered your account with BlendIt and MXit!\n");
      else:
         message = _("You have successfully registered your account with BlendIt!\n");

      message += _("These details have been stored in the BlendIt's settings file. You may now log in to MXit by clicking 'Apply'\n");

      label = gtk.Label(message);
      label.show();
      page.pack_start(label,True,False,16);
      page.show();
      
      self.append_page(page);
      self.set_page_title(page,_("Complete!"))
      self.set_page_type(page,gtk.ASSISTANT_PAGE_CONFIRM);


   ### Dialog Boxes ###
   def errorMessageDialog(self,message):
      # Yeah...
      self.blend.gui.createErrorMessage(self,message);


   ### DATA Methods ###
   def dateFormat(self,cal):
      year,month,day = cal.get_date();
      return "%s-%s-%d" % (year,month+1,day);

   def timeout(self):
      self.errorMessageDialog(_("Could not connect to MXit. Please try again later."));
      return False;

   def register(self):
      if self.loginName == "":
         login = self.entries[1].get_text();
      else:
         login = self.loginName;
      pin = self.entries[3].get_text();
      name = c.MxitConnection().ee(self.entries[2].get_text());
      dob = self.dateFormat(self.entries[6]);
      
      # Gender
      if self.entries[7][0].get_active():
         gender = 1;
      else:
         gender = 0;
      
      # Country
      model = self.entries[4].get_model();
      iter = self.entries[4].get_active_iter();
      loc = model.get_value(iter,1);
      
      # Language
      model = self.entries[5].get_model();
      iter = self.entries[5].get_active_iter();
      lang = model.get_value(iter,1);

      # Features:
      feats = 107051;
      
      pin = encryption.encode_password(self.code,pin);
      code = encryption.client_id(self.code);
      
      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
      sock.connect(("stream.mxit.co.za", 9119));
      cmd = "id=%s\000cm=11\000ms=%s\001C-5.8.2-Y-linux\0011024\001%s\001%s\001%s\001%s\001%s\001%s\001%s\001%s\001%s\000";
      cmd = cmd % (login,pin,name,dob,gender,loc,'ver=5.8.2;cat=Y',code,feats,self.CCode,lang);
      l = len(cmd);
      if debug == True:
         print 'ln=' + str(l) + "\\000" + cmd.replace("\000","\\000").replace("\001","\\001");
      sock.send('ln=' + str(l) + "\000" + cmd);
      to = gobject.timeout_add(10000,self.timeout);
      listen = True;
      data = "";
      while listen == True:
         data += sock.recv(1);
         if data.endswith("\000"):
            ln = data.split("\000")[0][3:];
            chars = len(data.split("\000")[0])+1;

           
            data += sock.recv(int(ln));
            data = data[chars:];
            listen = False;
      gobject.source_remove(to);
      # Get stuff
      data = data.split("\000");
      if int(data[0]) == 11:
         err = int(data[1].split("\001")[0]);
         if err == 0:
            # yay
            loginName = data[3].split("\001")[1];
            self.entries[1].set_text(loginName);
            sock.close();
            return True;
         else:
            eMsg = data[1].split("\001")[1];
            self.errorMessageDialog(_("Error while registering: %(errorMessage)s") % {'errorMessage': eMsg});
      else:
         self.errorMessageDialog(_("An unknown error occured!"));

      sock.close();
      return False;

   def store(self):
      cfg = config.config();

      model = self.entries[5].get_model();
      iter = self.entries[5].get_active_iter();
      lang = model.get_value(iter,1);
      
      acc = account.Account('me',self.loginName,self.entries[3].get_text(),
                            self.code,True,lang);

      cObj = cfg.getConfig();
      cfg.save(cObj,acc);

   def createAccount(self):
      loginOrRegister = '1';
      if self.entries[0].get_active():
         loginOrRegister = '0';

      iter = self.entries[4].get_active_iter();
      model = self.entries[4].get_model();
      country = model.get_value(iter,1);
      
      iter = self.entries[5].get_active_iter();
      model = self.entries[5].get_model();
      lang = model.get_value(iter,1);

      params = 'sessionid=%s&ver=C-5.8.2-Y-linux&login=%s&cat=Y&chalresp=%s&cc=%s&loc=%s&path=%s&brand=PC&model=blendit';
      params = params % (self.sess,self.entries[1].get_text(),self.captchaEntry.get_text(),
                         country,lang,loginOrRegister);

      self.getData('getpid',params);
      result = self.parseResponse();
      if result == True:
         # Cool.
         if loginOrRegister == '0':
            # This means we must connect to the mxit socket and register...
            if self.register():
               self.store();
            else:
               gtk.Assistant.set_current_page(self,1);
         else:
            # We must store the data in the settings.xml
            self.store();
         return 0;
      elif result == False:
         self.errorMessageDialog(_("The captcha you entered was incorrect"));
         # Captcha wrong. Reset the captcha and display step 2
         return 2;
      elif result == -1:
         # Unknown error
         self.errorMessageDialog(_("An unknown error occured"));
         # Go back to step 1
         return 2;
      elif result == -2:
         # User not registered
         self.errorMessageDialog(_("You are not yet registered with MXit"));
         # Go back to step 1
         return 1;
      elif result == -3:
         # Already registered
         self.errorMessageDialog(_("You are already registered with MXit"));
         # Go back to step 1
         return 1;


   def parseResponse(self):
      values = self.data.split(";");
      if int(values[0]) == 1:
         # Wrong answer (sends new captcha)
         self.saveCaptcha(values[1],self.sess);
         return False;
      elif int(values[0]) == 2:
         if len(values) == 3:
            self.sess = values[1];
            self.saveCaptcha(values[2],self.sess);
         else:
            self.saveCaptcha(values[2]);
         return False;
      elif int(values[0]) in (3,4,5):
         return -1;
      elif int(values[0]) == 6:
         # User not registered
         self.sess = values[1];
         self.saveCaptcha(values[2],self.sess);
         return -2;
      elif int(values[0]) == 7:
         # Already registered
         self.sess = values[1];
         self.saveCaptcha(values[2],self.sess);
         return -3;

      # Parse more data
      self.code = values[1];
      self.loginName = values[10];
      
      return True;
       
   
   # 'challenge','getcountries=true&getlanguage=true'
   def getData(self,type,params):
      tt = int(time.time());
      request = "type=%s&%s&ts=%s" % (type,params,tt);
      res = urllib.FancyURLopener();
      d = res.open("http://www.mxit.com/res/?" + request);
      data = d.read();
      d.close();
      res.close();
      
      self.data = data;

   def parseData(self):
      values = self.data.split(";");
      err = values[0];
      if int(err) != 0:
         return False;

      self.pidURL = values[1];
      self.sess = values[2];
      self.saveCaptcha(values[3],values[2]);
      self.countries = self.parseList(values[4]);
      self.languages = self.parseList(values[5]);
      self.defaultCountry = self.search(self.countries,values[7]);
      self.CCode = values[9];
      
      return True;

   def saveCaptcha(self,data,session):
      data = base64.b64decode(data);
      f = open(os.path.join(self.blend.temp,'%s.png'%session),'wb');
      f.write(data);
      f.close();

   def search(self,list,search,key=True,value=False):

      for i in range(0,len(list)):
         if key == True:
            if list[i][1] == search:   
               return i;
         if value == True:
            if list[i][0] == search:
               return i;

      return -1;

   def parseList(self,data):
      list = []
      items = data.split(",");

      for i in range(0,len(items)):
         #key,value = items[i].split("|");
         key,value = items[i].split("|")
         list.append((value,key));

      list.sort();
      return list;
      
      
